"""
Live2D渲染引擎
负责渲染虚拟形象并同步动画
支持 Web 版渲染（通过 WebSocket 通信）
"""

import os
import time
import math
import random
import webbrowser
import subprocess
from pathlib import Path
from typing import Optional, Tuple

import pygame
from pygame.locals import *

from ..core.models import Live2DParams, EmotionType, LipSyncData
from ..utils.logger import get_logger
from .websocket_server import WebSocketServer


class Live2DRenderer:
    """Live2D渲染引擎"""

    def __init__(
        self,
        model_path: str,
        window_size: Tuple[int, int] = (1920, 1080),
        fps: int = 60,
        background_color: Tuple[int, int, int] = (0, 255, 0),  # 绿幕
        use_web_renderer: bool = True,  # 使用 Web 渲染器
        auto_open_browser: bool = True  # 是否自动打开浏览器
    ):
        """
        初始化Live2D渲染引擎
        
        Args:
            model_path: Live2D模型路径
            window_size: 渲染窗口大小(宽, 高)
            fps: 渲染帧率
            background_color: 背景颜色(RGB)
            use_web_renderer: 是否使用 Web 渲染器（推荐）
        """
        self.model_path = model_path
        self.window_size = window_size
        self.fps = fps
        self.background_color = background_color
        self.use_web_renderer = use_web_renderer
        self.auto_open_browser = auto_open_browser  # 保存参数

        self.logger = get_logger()

        # Live2D参数
        self.params = Live2DParams()

        # 渲染状态
        self.is_running = False
        self.window = None
        self.clock = None
        
        # WebSocket 服务器（用于 Web 渲染）
        self.ws_server = None
        if use_web_renderer:
            self.ws_server = WebSocketServer()

        # 动画控制
        self.blink_timer = 0.0
        self.blink_interval = random.uniform(3.0, 6.0)  # 随机眨眼间隔
        self.head_sway_time = 0.0
        self.body_sway_time = 0.0
        
        # 口型同步状态
        self.is_speaking = False  # 是否正在说话（口型同步中）

        # 情感状态
        self.current_emotion = EmotionType.NEUTRAL
        self.emotion_timer = 0.0
        self.emotion_duration = 3.0  # 情感持续时间

    def initialize(self) -> bool:
        """
        初始化渲染引擎
        
        Returns:
            是否初始化成功
        """
        try:
            self.logger.info("正在初始化Live2D渲染引擎...")
            
            if self.use_web_renderer:
                # 使用 Web 渲染器，传递 auto_open_browser 参数
                return self._initialize_web_renderer(auto_open_browser=self.auto_open_browser)
            else:
                # 使用传统 Pygame 渲染器（占位符）
                return self._initialize_pygame_renderer()

        except Exception as e:
            self.logger.error(f"初始化Live2D渲染引擎失败: {e}")
            return False
    
    def _initialize_web_renderer(self, auto_open_browser: bool = True) -> bool:
        """
        初始化 Web 渲染器
        
        Args:
            auto_open_browser: 是否自动打开浏览器（默认True，可通过参数禁用避免重复打开）
        
        Returns:
            是否初始化成功
        """
        try:
            # 检查模型路径
            if not os.path.exists(self.model_path):
                self.logger.error(f"Live2D模型路径不存在: {self.model_path}")
                return False
            
            # 查找模型文件
            model_json = self._find_model_json()
            if not model_json:
                self.logger.error("未找到 .model3.json 文件")
                return False
            
            self.logger.info(f"✓ Live2D模型文件: {model_json.name}")
            self.logger.info(f"✓ Live2D模型路径: {self.model_path}")
            
            # 启动 WebSocket 服务器
            self.ws_server.start()
            time.sleep(0.5)  # 等待服务器启动
            
            # 只在需要时打开浏览器
            if auto_open_browser:
                html_path = Path(__file__).parent.parent.parent / "web_live2d_cubism4.html"
                if html_path.exists():
                    self.logger.info(f"正在打开 Live2D 渲染页面...")
                    # 使用 HTTP 服务器地址而不是 file:// 协议
                    webbrowser.open("http://localhost:8000/web_live2d_cubism4.html")
                    self.logger.info("✓ Live2D Web 渲染器已启动")
                    self.logger.info("访问地址: http://localhost:8000/web_live2d_cubism4.html")
                else:
                    self.logger.error(f"未找到 HTML 文件: {html_path}")
                    return False
            else:
                self.logger.info("✓ Live2D Web 渲染器已启动（浏览器已由外部打开）")
                self.logger.info("访问地址: http://localhost:8000/web_live2d_cubism4.html")
            
            self.logger.info("✓ Live2D渲染引擎初始化成功（Web模式）")
            return True
            
        except Exception as e:
            self.logger.error(f"Web 渲染器初始化失败: {e}")
            return False
    
    def _initialize_pygame_renderer(self) -> bool:
        """
        初始化 Pygame 渲染器（占位符模式）
        
        Returns:
            是否初始化成功
        """
        try:
            # 初始化Pygame
            pygame.init()

            # 创建窗口
            self.window = pygame.display.set_mode(self.window_size)
            pygame.display.set_caption("AI VTuber - Live2D")

            # 创建时钟
            self.clock = pygame.time.Clock()

            # 检查模型路径
            if not os.path.exists(self.model_path):
                self.logger.warning(f"Live2D模型路径不存在: {self.model_path}")
                self.logger.warning("将使用简化渲染模式(占位符)")
            else:
                self.logger.info(f"✓ Live2D模型路径: {self.model_path}")

            self.logger.info("✓ Live2D渲染引擎初始化成功（Pygame模式）")
            return True
            
        except Exception as e:
            self.logger.error(f"Pygame 渲染器初始化失败: {e}")
            return False
    
    def _find_model_json(self) -> Optional[Path]:
        """
        查找模型配置文件
        
        Returns:
            模型配置文件路径，未找到返回 None
        """
        model_dir = Path(self.model_path)
        
        # 查找 .model3.json 文件
        json_files = list(model_dir.glob("*.model3.json"))
        
        if json_files:
            return json_files[0]
        
        return None

    def start(self):
        """启动渲染循环"""
        self.is_running = True
        self.logger.info("Live2D渲染循环已启动")
        
        if self.use_web_renderer:
            # Web 渲染器不需要 Pygame 循环，只需要更新参数
            self._start_web_loop()
        else:
            # Pygame 渲染器需要事件循环
            self._start_pygame_loop()
    
    def _start_web_loop(self):
        """启动 Web 渲染器的参数更新循环"""
        try:
            while self.is_running:
                # 更新动画参数
                self._update_animations()
                
                # 发送参数到 Web 渲染器
                self._send_params_to_web()
                
                # 控制帧率
                time.sleep(1.0 / self.fps)
                
        except Exception as e:
            self.logger.error(f"渲染循环异常: {e}")
        finally:
            self.cleanup()
    
    def _start_pygame_loop(self):
        """启动 Pygame 渲染器的事件循环"""
        try:
            while self.is_running:
                # 处理事件
                for event in pygame.event.get():
                    if event.type == QUIT:
                        self.stop()

                # 更新动画参数
                self._update_animations()

                # 渲染帧
                self._render_frame()

                # 控制帧率
                if self.clock:
                    self.clock.tick(self.fps)

        except Exception as e:
            self.logger.error(f"渲染循环异常: {e}")
        finally:
            self.cleanup()
    
    def _send_params_to_web(self):
        """发送参数到 Web 渲染器"""
        if not self.ws_server:
            return
        
        # 构建参数字典
        params = {
            'mouthOpen': self.params.mouth_open_y,
            'angleX': self.params.angle_x,
            'angleY': self.params.angle_y,
            'eyeLOpen': self.params.eye_l_open,
            'eyeROpen': self.params.eye_r_open,
            'emotion': self.params.emotion.value
        }
        
        # 发送到所有连接的客户端
        self.ws_server.update_params(params)

    def stop(self):
        """停止渲染"""
        self.is_running = False
        self.logger.info("正在停止Live2D渲染...")

    def cleanup(self):
        """清理资源"""
        if self.use_web_renderer and self.ws_server:
            self.ws_server.stop()
            self.logger.info("✓ WebSocket 服务器已关闭")
        
        if self.window:
            pygame.quit()
        
        self.logger.info("✓ Live2D渲染引擎已关闭")

    def _update_animations(self):
        """更新动画参数"""
        delta_time = 1.0 / self.fps

        # 更新眨眼动画
        self._update_blink(delta_time)

        # 更新头部摇摆
        self._update_head_sway(delta_time)

        # 更新身体晃动
        self._update_body_sway(delta_time)

        # 更新情感状态
        self._update_emotion(delta_time)
        
        # 更新嘴巴状态（自动衰减到闭合）
        self._update_mouth(delta_time)

    def _update_blink(self, delta_time: float):
        """更新眨眼动画"""
        # 说话时减少眨眼频率，避免影响表现力
        if self.is_speaking:
            # 说话时只在极少情况下眨眼（间隔拉长到10-15秒）
            max_blink_interval = 15.0
            min_blink_interval = 10.0
        else:
            # 不说话时正常眨眼（3-6秒）
            max_blink_interval = 6.0
            min_blink_interval = 3.0
        
        self.blink_timer += delta_time

        if self.blink_timer >= self.blink_interval:
            # 触发眨眼
            self.params.eye_l_open = 0.0
            self.params.eye_r_open = 0.0
            self.blink_timer = 0.0
            self.blink_interval = random.uniform(min_blink_interval, max_blink_interval)
        elif self.blink_timer < 0.15:
            # 眨眼持续0.15秒
            self.params.eye_l_open = 0.0
            self.params.eye_r_open = 0.0
        else:
            # 睁开眼睛
            self.params.eye_l_open = 1.0
            self.params.eye_r_open = 1.0

    def _update_head_sway(self, delta_time: float):
        """更新头部轻微摇摆"""
        self.head_sway_time += delta_time

        # 使用正弦波生成自然的头部晃动
        self.params.angle_x = math.sin(self.head_sway_time * 0.5) * 5  # -5到5度
        self.params.angle_y = math.cos(self.head_sway_time * 0.3) * 3  # -3到3度

    def _update_body_sway(self, delta_time: float):
        """更新身体轻微晃动"""
        self.body_sway_time += delta_time

        # 身体晃动频率更慢
        self.params.body_angle_x = math.sin(self.body_sway_time * 0.4) * 3  # -3到3度

    def _update_emotion(self, delta_time: float):
        """更新情感状态"""
        if self.params.emotion != EmotionType.NEUTRAL:
            self.emotion_timer += delta_time

            # 情感持续一段时间后恢复中性
            if self.emotion_timer >= self.emotion_duration:
                self.set_emotion(EmotionType.NEUTRAL)
                self.emotion_timer = 0.0
    
    def _update_mouth(self, delta_time: float):
        """更新嘴巴状态（自动衰减到闭合）"""
        # 只在不说话时才自动衰减
        if not self.is_speaking:
            # 如果嘴巴是张开的，逐渐衰减到闭合状态
            if self.params.mouth_open_y > 0.01:
                # 衰减速度：每秒减少 3.0（即0.33秒内从1.0衰减到0）
                decay_speed = 3.0
                self.params.mouth_open_y = max(0.0, self.params.mouth_open_y - decay_speed * delta_time)
            else:
                # 已经接近闭合，直接设为0
                self.params.mouth_open_y = 0.0

    def _render_frame(self):
        """渲染一帧"""
        try:
            # 填充背景色(绿幕)
            self.window.fill(self.background_color)

            # TODO: 这里应该调用实际的Live2D SDK渲染
            # 由于Live2D SDK在Python中集成较复杂,这里使用占位符
            self._render_placeholder()

            # 显示参数信息(调试用)
            self._render_debug_info()

            # 更新显示
            pygame.display.flip()

        except Exception as e:
            self.logger.error(f"渲染帧失败: {e}")

    def _render_placeholder(self):
        """渲染占位符(简化的虚拟形象)"""
        # 获取窗口中心
        center_x = self.window_size[0] // 2
        center_y = self.window_size[1] // 2

        # 根据情感选择颜色
        emotion_colors = {
            EmotionType.NEUTRAL: (200, 200, 200),
            EmotionType.HAPPY: (255, 200, 100),
            EmotionType.SURPRISED: (255, 150, 150),
            EmotionType.CONFUSED: (150, 150, 255),
            EmotionType.SAD: (150, 200, 255),
            EmotionType.EXCITED: (255, 100, 100)
        }
        color = emotion_colors.get(self.params.emotion, (200, 200, 200))

        # 绘制脸部(圆形)
        face_x = int(center_x + self.params.angle_x * 3)
        face_y = int(center_y + self.params.angle_y * 3)
        pygame.draw.circle(self.window, color, (face_x, face_y), 150, 0)

        # 绘制眼睛
        eye_l_x = face_x - 50
        eye_r_x = face_x + 50
        eye_y = face_y - 30
        eye_size = int(20 * self.params.eye_l_open)

        if eye_size > 0:
            pygame.draw.circle(self.window, (0, 0, 0), (eye_l_x, eye_y), eye_size, 0)
            pygame.draw.circle(self.window, (0, 0, 0), (eye_r_x, eye_y), eye_size, 0)

        # 绘制嘴巴
        mouth_y = face_y + 40
        mouth_open_height = int(30 * self.params.mouth_open_y)
        if mouth_open_height > 0:
            pygame.draw.ellipse(
                self.window,
                (255, 100, 100),
                (face_x - 30, mouth_y, 60, mouth_open_height),
                0
            )
        else:
            # 闭嘴时画一条线
            pygame.draw.line(
                self.window,
                (0, 0, 0),
                (face_x - 30, mouth_y),
                (face_x + 30, mouth_y),
                3
            )

    def _render_debug_info(self):
        """渲染调试信息"""
        font = pygame.font.Font(None, 24)
        y_offset = 10

        debug_info = [
            f"FPS: {int(self.clock.get_fps())}",
            f"Emotion: {self.params.emotion.value}",
            f"Mouth Open: {self.params.mouth_open_y:.2f}",
            f"Eye Open: {self.params.eye_l_open:.2f}",
            f"Angle X: {self.params.angle_x:.1f}",
        ]

        for info in debug_info:
            text = font.render(info, True, (255, 255, 255))
            self.window.blit(text, (10, y_offset))
            y_offset += 25

    def update_mouth(self, lipsync_data: LipSyncData):
        """
        更新嘴巴张开度(用于口型同步)
        
        Args:
            lipsync_data: 口型同步数据
        """
        self.params.mouth_open_y = lipsync_data.mouth_open
        self.is_speaking = True  # 标记为说话状态
        
        # 如果是 Web 渲染器，立即发送更新
        if self.use_web_renderer and self.ws_server:
            # 确保WebSocket服务器已连接
            if self.ws_server.has_clients():
                self._send_params_to_web()
            else:
                # 首次调用时记录警告
                if not hasattr(self, '_ws_warning_logged'):
                    self.logger.warning("⚠️ WebSocket客户端未连接，无法同步口型")
                    self.logger.warning("   请确保浏览器已打开 http://localhost:8000/web_live2d_cubism4.html")
                    self._ws_warning_logged = True

    def set_emotion(self, emotion: EmotionType):
        """
        设置情感状态
        
        Args:
            emotion: 情感类型
        """
        if self.params.emotion != emotion:
            self.params.emotion = emotion
            self.emotion_timer = 0.0
            self.logger.debug(f"切换情感: {emotion.value}")
            
            # 如果是 Web 渲染器，立即发送更新
            if self.use_web_renderer:
                self._send_params_to_web()

    def get_current_params(self) -> Live2DParams:
        """获取当前Live2D参数"""
        return self.params

    def reset_mouth(self):
        """重置嘴巴为闭合状态"""
        self.params.mouth_open_y = 0.0
        self.is_speaking = False  # 标记为非说话状态
        
        # 立即发送更新
        if self.use_web_renderer:
            self._send_params_to_web()
